﻿//     Copyright (c) Microsoft Corporation.  All rights reserved.

using Microsoft.Gateway.Configuration;
using Microsoft.Gateway.Utils;
using System;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Microsoft.Gateway.RulesConfigurator
{
    class AuthorizationConfigurator
    {
        public static ConfiguratorResults ApplyConfiguration(RulesConfiguration configToApply, string configPath)
        {
            try
            {
                if (String.IsNullOrWhiteSpace(configPath) && !AzureUtils.TryGetConfigPath(out configPath))
                {
                    Trace.TraceInformation("AuthorizationConfigurator: website not available yet, so not running configuration.");
                    return ConfiguratorResults.RetryLater;
                }
                XDocument webConfig = XDocument.Load(configPath);

                var existingDenys = webConfig.XPathSelectElements("//configuration/location/system.web/authorization/deny").ToArray();
                var root = webConfig.XPathSelectElement("//configuration");

                foreach (var deny in existingDenys)
                {
                    //deny/authorization/system.web/location
                    deny.Parent.Parent.Parent.Remove();
                }

                if (string.IsNullOrEmpty(configToApply.ApplicationName) || string.IsNullOrEmpty(configToApply.Directory))
                {
                    SetGlobalAccess(webConfig, true);
                }
                else if (configToApply.Roles.Any(r => r.Value.RequireAuthenticationBelow.Count > 0))
                {
                    foreach (var role in configToApply.Roles)
                    {
                        var name = role.Key;
                        if (role.Value.RequireAuthenticationBelow.Count > 0)
                        {
                            foreach (var deny in role.Value.RequireAuthenticationBelow)
                            {
                                var path = role.Value.IsDefaultRole ? deny :
                                    String.IsNullOrWhiteSpace(deny) ? name : name + "/" + deny;
                                AddDenyRule(webConfig, path);
                            }
                        }
                    }
                    SetGlobalAccess(webConfig, true);
                }
                else
                {
                    SetGlobalAccess(webConfig, false);
                }

                webConfig.Save(configPath);
                Trace.TraceInformation("AuthorizationConfigurator::ApplyConfiguration: " + webConfig.ToString());
                return ConfiguratorResults.Success;
            }
            catch (Exception e)
            {
                Trace.TraceError("Exception in AuthorizationConfigurator::ApplyConfiguration: " + e.ToString() + e.StackTrace);
                return ConfiguratorResults.Failure;
            }
        }
        private static void SetGlobalAccess(XDocument webConfig, bool allow)
        {
            var sysConfigurationElement = webConfig.XPathSelectElement("//configuration");

            var sysWebElement = sysConfigurationElement.XPathSelectElement("system.web");
            if (sysWebElement == null)
            {
                sysConfigurationElement.Add(new XElement(XNamespace.None + "system.web"));
                sysWebElement = sysConfigurationElement.XPathSelectElement("system.web");
            }
            var authElement = sysWebElement.XPathSelectElement("authorization");
            if (authElement == null)
            {
                sysWebElement.Add(new XElement(XNamespace.None + "authorization"));
                authElement = sysWebElement.XPathSelectElement("authorization");
            }
            authElement.ReplaceAll(new XElement(XNamespace.None + (allow ? "allow" : "deny"),
                                                    new XAttribute(XNamespace.None + "users", allow ? "*" : "?")));
        }
        private static void AddDenyRule(XDocument webConfig, string path)
        {
            webConfig.XPathSelectElement("//configuration/configSections").AddAfterSelf(
                new XElement(XNamespace.None + "location",
                    new XAttribute(XNamespace.None + "path", path),
                    new XElement(XNamespace.None + "system.web",
                        new XElement(XNamespace.None + "authorization",
                              new XElement(XNamespace.None + "deny",
                                  new XAttribute(XNamespace.None + "users", "?"))))));
        }
    }
}
